package org.example.controller;

import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.example.common.entity.User;
import org.example.service.UserService;
import org.example.util.WXDecryptUtil;
import org.example.util.WxSessionService;
import org.example.util.session.SessionException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import java.net.URLEncoder;
import java.time.Instant;
import java.sql.Timestamp;
import java.util.Map;
import java.util.Optional;

@Slf4j
@RestController
@RequestMapping("/api/wx")
public final class AuthController {

    private static final String WX_API_URL = "https://api.weixin.qq.com/sns/jscode2session";

    // 微信登录响应
    private record WxLoginResponse(String openid, String session_key, String errcode, String errmsg) {
        static WxLoginResponse fromJson(String json) {
            return JSON.parseObject(json, WxLoginResponse.class);
        }
        
        boolean hasError() {
            return errcode != null && !errcode.equals("0");
        }
        
        boolean isValid() {
            return openid != null && session_key != null;
        }
    }

    private final String appId;
    private final String appSecret;
    private final UserService userService;
    private final WxSessionService sessionService;

    public AuthController(
            @Value("${wx.miniapp.appid}") String appId,
            @Value("${wx.miniapp.secret}") String appSecret,
            UserService userService,
            WxSessionService sessionService) {
        this.appId = appId;
        this.appSecret = appSecret;
        this.userService = userService;
        this.sessionService = sessionService;
    }

    @PostMapping("/login")
    public SaResult login(@RequestBody Map<String, String> params ) {
        var code = params.get("code");
        if (code == null || code.trim().isEmpty()) {
            return SaResult.error("登录失败：code不能为空");
        }

        try (var httpClient = HttpClients.createDefault()) {
            // 构建URL
            String url = String.format("%s?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code",
                    WX_API_URL, appId, appSecret, URLEncoder.encode(code, "UTF-8"));

            try (var response = httpClient.execute(new HttpGet(url))) {
                var responseBody = EntityUtils.toString(response.getEntity());
                var wxResponse = WxLoginResponse.fromJson(responseBody);

                if (wxResponse.hasError()) {
                    log.error("微信登录失败: errcode={}, errmsg={}",
                            wxResponse.errcode(), wxResponse.errmsg());
                    return SaResult.error("登录失败：" + wxResponse.errmsg());
                }

                if (!wxResponse.isValid()) {
                    return SaResult.error("登录失败：获取openid或session_key失败");
                }

                // 将 session_key 存储到缓存中
                try {
                    sessionService.saveSessionKey(wxResponse.openid(), wxResponse.session_key());
                } catch (SessionException e) {
                    log.error("保存会话密钥失败", e);
                    return SaResult.error("登录失败：" + e.getMessage());
                }

                // 检查用户是否存在并创建或获取用户
                var user = Optional.ofNullable(userService.selectUserByUsername(new User().setOpenId(wxResponse.openid())))
                        .orElseGet(() -> createNewUser(wxResponse.openid()
                        , StpUtil.getTokenValue()
                        ));

                // 使用 Sa-Token 进行登录
                StpUtil.login(wxResponse.openid());
                StpUtil.login(
                        user.getUserId()
                );
                return SaResult.data(user);
            }
        } catch (Exception e) {
            log.error("登录过程发生错误", e);
            return SaResult.error("登录失败：" + e.getMessage());
        }
    }


    private User createNewUser(String openid,String token) {
        var now = Timestamp.from(Instant.now());
        User user = new User()
                .setTenantId(1L)
                .setOpenId(openid)
                .setUsername("")
                .setPassword("")
                .setFullName("")
                .setEmail("")
                .setPhoneNumber("")
                .setStatus("active")
                .setCreatedAt(now)
                .setUpdatedAt(now)
                .setTokenValue(token);
        userService.insertUserWithOpenId(user);
        return user;
    }

    @PostMapping("/getUserInfo")
    public SaResult getUserInfo(@RequestBody Map<String, String> params) {
        if (!StpUtil.isLogin()) {
            return SaResult.error("未登录");
        }

        try {
            record UserInfoRequest(String encryptedData, String iv) {
                static UserInfoRequest from(Map<String, String> params) {
                    return new UserInfoRequest(
                        params.get("encryptedData"),
                        params.get("iv")
                    );
                }

                boolean isValid() {
                    return encryptedData != null && iv != null;
                }
            }

            var request = UserInfoRequest.from(params);
            if (!request.isValid()) {
                return SaResult.error("参数错误：缺少必要的加密数据");
            }

            var openid = StpUtil.getLoginIdAsString();
            var sessionKey = sessionService.getSessionKey(openid)
                    .orElse(null);

            if (sessionKey == null) {
                return SaResult.error("登录已过期，请重新登录");
            }

            // 刷新会话
            sessionService.refreshSession(openid);

            var remainingTime = sessionService.getSessionRemainingTime(openid);
            log.debug("当前会话剩余有效时间: {}秒", remainingTime.toSeconds());

            return Optional.ofNullable(WXDecryptUtil.decrypt(request.encryptedData(), sessionKey, request.iv()))
                    .map(JSON::parseObject)
                    .map(SaResult::data)
                    .orElse(SaResult.error("解密失败"));

        } catch (Exception e) {
            log.error("获取用户信息失败", e);
            return SaResult.error("获取用户信息失败：" + e.getMessage());
        }
    }
}